from typing import Optional, List, Dict

from . import buffer_pool
from .page import Page, PageType, DataPage
from .data_item import DataItem
from .buffer_pool import BufferPoolManager
from .disk_manager import DiskManager
from .log_manager import RedoLogManager as LogManager
from index.sub_array import SubArray

from utils.parser import Parser


class DataManager:
    """数据管理器接口
    
    统一管理数据的读取、插入和存储操作
    """

    def __init__(self, buffer_pool: BufferPoolManager, log_manager: LogManager):
        self.buffer_pool = buffer_pool
        self.log_manager = log_manager
        self.data_item_cache: Dict[int, DataItem] = {}

    # 创造
    @classmethod
    def create(cls, path: str, memory: int) -> 'DataManager':
        """创建数据管理器"""
        # 创建
        disk_manager = DiskManager(path)
        # 注入
        buffer_pool = BufferPoolManager(memory // Page.DEFAULT_PAGE_SIZE)
        buffer_pool.set_disk_manager(disk_manager)
        # 同一个disk_manager
        log_manager = LogManager(disk_manager)  # 传递disk_manager对象而不是path字符串
        return cls(buffer_pool, log_manager)

    # 打开现有数据库【语义区分】disk_manager已有若不存在则创建
    @classmethod
    def open(cls, path: str, memory: int) -> 'DataManager':
        """打开现有数据管理器"""
        # 与 create 相同，但用于语义区分
        return cls.create(path, memory)

    # 读取
    # 一个返回DataItem的数据项，根据uid获取
    def read(self, uid: int) -> Optional[DataItem]:
        """读取数据项"""
        if uid in self.data_item_cache:
            data_item =self.data_item_cache[uid]
            if data_item.is_valid():
                return data_item
            else:
                data_item.release()
                return None
        # 若无则让buffer_pool去取
        data_item= self._get_for_cache(uid)
        # 若为合法则
        if data_item.is_valid():
            self.data_item_cache[uid] = data_item
            return data_item
        return None

    def read_without_valid_check(self, uid: int) -> Optional[DataItem]:
        """读取数据项"""
        if uid in self.data_item_cache:
            data_item =self.data_item_cache[uid]
            if data_item.is_valid():
                return data_item
            else:
                data_item.release()
                return None
        # 若无则让buffer_pool去取
        data_item= self._get_for_cache(uid)
        # 若为合法则
        return data_item

    # 删除
    def delete(self, xid: int, uid: int):
        data_item = self.read(uid)
        # 设置非法
        data_item.delete()
        # 去除缓存
        self.data_item_cache.pop(uid)
        # page_id的使用-1
        page_id,_=Parser.uid_to_address(uid)
        self.buffer_pool.unpin_page(page_id, is_dirty=True)

    def _get_for_cache(self, uid: int) -> Optional[DataItem]:
        # 从UID解析页面ID和偏移量：高16位为页号，低16位为偏移
        page_id,offset= Parser.uid_to_address(uid)
        pg=self.buffer_pool.fetch_page(page_id)
        return  DataItem.parse_data_item(pg,offset,self)

    # 插入
    def insert(self, xid: int, data: bytes) -> int:
        """插入数据项"""
        # DataItem 原始格式：[valid(1)][size(2)][data]
        raw = DataItem.wrap_data_item_raw(data)

        # 获取可用数据页
        page = self._get_available_page(len(raw))
        if page is None or not isinstance(page, DataPage):
            return -1

        # 在数据页中插入记录
        offset = page.insert_record(raw)
        uid = (page.page_id << 16) | offset

        # 记录插入日志（如果启用日志）
        self.log_manager.log_insert(xid, page.page_id, offset, raw)
        
        # 插入完成后立即释放页面引用（标记为脏页）
        self.buffer_pool.unpin_page(page.page_id, is_dirty=True)

        return uid


    # 日志
    def log_data_item(self, xid: int, data_item: DataItem) -> None:
        """记录数据项日志"""
        pass
        # self.log_manager.log_update(xid, data_item.get_uid(),
        #                             data_item.old_raw, data_item.raw)

    def release_data_item(self, data_item: DataItem) -> None:
        """释放数据项"""
        if data_item.get_uid() in self.data_item_cache:
            del self.data_item_cache[data_item.get_uid()]
        
        # 归还页面的引用计数
        page = getattr(data_item, "page", None)
        if page is not None:
            # 如果页面有 get_page_number 则优先使用
            page_id = page.get_page_number() if hasattr(page, "get_page_number") else getattr(page, "page_id", None)
            if page_id is not None:
                self.buffer_pool.unpin_page(page_id)

    def _get_available_page(self, size: int) -> Optional[Page]:
        """获取可用页面：优先复用已有 DataPage 的空闲空间，找不到再分配新页"""

        # 遍历现有页，查找可用 DataPage（通过 BufferPool 的 DiskManager 获取页总数）
        dm = getattr(self.buffer_pool, "disk_manager", None)

        if dm is not None and hasattr(dm, "num_pages"):
            for pid in range(dm.num_pages):
                page = self.buffer_pool.fetch_page(pid)
                if page is None:
                    continue
                try:
                    if isinstance(page, DataPage) and page.get_free_space_size() >= size:
                        return page  # 找到可用页（保持 pin，供上层写入）
                finally:
                    # 未选中的页需要及时 unpin，避免 pin 泄漏
                    if not (isinstance(page, DataPage) and page.get_free_space_size() >= size):
                        self.buffer_pool.unpin_page(page.page_id)

        # 没有找到可用页，分配新页
        return self.buffer_pool.new_page(PageType.DATA)

    def _insert_into_page(self, page: Page, raw: bytes) -> int:
        """在页面中插入数据"""
        if isinstance(page, DataPage):
            offset = page.insert_record(raw)

            return offset
        # # 兜底：非数据页的极简追加（不建议走到这里）
        # data = page.get_data()
        # offset = len([b for b in data if b != 0])  # 找到第一个空位置
        # data[offset:offset + len(raw)] = raw
        # page.set_dirty(True)
        # return offset

    # 关闭
    def close(self) -> None:
        """关闭数据管理器"""
        self.buffer_pool.flush_all_pages()
        self.log_manager.close()
